#include "generate_file.h"

#include <QDebug>
#include <QFile>
#include <QMessageBox>
#include <QRegExp>

const QString generate_file::REPLACE_NAME_FLAG = "[name]";        // 用于替换模板文件中的名字
const QString generate_file::REPLACE_SCORE_FLAG = "[score]";      // 用于替换模板文件中的分数
const QString generate_file::REPLACE_CATCH_NUM_REGEXP = "^\\d+";  // 学生数据文件中捕获分数的正则表达式
const QString generate_file::REPLACE_CHECK_DATA_REGEXP =
    "^[\u4e00-\u9fa5]{2,4}\\s\\d+(\\.\\d+)?(\\s\\d+(?:，\\d+)*)?\\r?\\n?$";  // 用于验证数据格式的正则表达式

generate_file::generate_file(QObject *parent, bool is_ignore) : QObject(parent) {
}

/**
 * @brief 析构函数
 *
 * 释放生成文件类的资源。
 */
generate_file::~generate_file() = default;

/**
 * @brief 读取模板文件
 *
 * 打开并读取指定路径的模板文件，解析内容并返回模型信息对象。
 *
 * @param model_file_path 模板文件路径
 * @return 返回模型信息指针，如果打开失败则返回 nullptr
 */
model_info *generate_file::_read_model_file(const QString &model_file_path) {
    // 打开模板文件
    QFile model_file(model_file_path);
    if (!model_file.open(QIODevice::ReadOnly)) {
        emit file_open_error(model_file_path);
        return nullptr;
    }

    emit send_message("正在读取模板文件...");

    // 读取模板文件
    auto read_all = QString::fromUtf8(model_file.readAll());
    auto context_list = read_all.split("_");
    // 发现模板文件中多余的分隔符
    if (context_list.size() > 3) {
        emit send_message("模板文件格式错误，多余的分隔符\"_\",生成失败!");
        return nullptr;
    }

    QRegExp reg(REPLACE_CATCH_NUM_REGEXP);
    QMap<QString, QString> error_map;
    auto item = context_list[1].split("`");
    for (int i = 1; i < item.size(); i++) {
        if (reg.indexIn(item[i]) != -1) {
            error_map.insert(reg.cap(0), item[i]);
        }
    }

    emit send_message("模板文件读取成功...");

    return (context_list.size() == 2)
               ? new model_info(context_list[0], error_map)
               : new model_info(context_list[0], error_map, context_list[2]);
}

/**
 * @brief 读取数据文件
 *
 * 打开并读取指定路径的数据文件，解析内容并返回学生信息数组。
 *
 * @param data_file_path 数据文件路径
 * @return 返回学生信息数组指针，如果打开失败则返回 nullptr
 */
QVector<student_info> *generate_file::_read_data_file(const QString &data_file_path) {
    // 打开数据文件
    QFile data_file(data_file_path);
    if (!data_file.open(QIODevice::ReadOnly)) {
        emit file_open_error(data_file_path);
        return nullptr;
    }

    emit send_message("开始读取数据文件...");

    // 读取数据文件
    auto *data = new QVector<student_info>;
    QVector<QString> error_data;
    bool is_pass = true;
    QRegExp reg(REPLACE_CHECK_DATA_REGEXP);  // 创建正则表达式对象

    while (!data_file.atEnd()) {
        QString line = QString::fromUtf8(data_file.readLine());  // 读取每一行数据
        bool ret = reg.exactMatch(line);                         // 验证数据格式
        if (!ret) {
            is_pass = false;
            error_data.push_back(line);
        } else {
            QStringList data_list = line.split(" ");  // 分隔数据
            const QString name = data_list[0];        // 获取单行数据的学生名字
            const QString score = data_list[1];       // 获取单行中学生成绩

            // 判断无错题情况
            QVector<unsigned short> error_list;
            if (data_list.size() == 3) {
                QStringList error_list_str = data_list[2].split("，");  // 拆分学生错题列表
                for (const auto &num : error_list_str) {
                    error_list.push_back(num.toUShort());
                }
            }
            data->push_back(student_info(name, score, error_list));
        }
    }

    if (!is_pass) {
        QString error_str = "数据格式错误，请检查以下数据：\n";
        for (const auto &line : error_data) {
            error_str += line;
        }
        emit send_message(error_str);
        emit send_message("数据文件格式有误，生成失败！");
        return nullptr;
    }

    emit send_message("数据文件读取完毕...");

    return data;
}

/**
 * @brief 根据模型和数据生成文件
 *
 * 根据给定的模型信息和学生信息，生成答复文件，并发送生成进度信号。
 *
 * @param model 模型信息指针
 * @param data 学生信息数组指针
 * @param gen_dir 生成文件的目录
 */
void generate_file::_generate_file(const model_info *model,
                                   const QVector<student_info> *data,
                                   const QString &gen_dir) {
    emit send_message("开始生成答复文件...");

    const unsigned int student_num = data->size();  // 学生个数

    for (unsigned int i = 0; i < student_num; i++) {
        QString name = data->at(i).get_name();                   // 获取学生姓名
        QString score = data->at(i).get_score().remove("\r\n");  // 获取学生成绩
        QString end = model->get_end();                          // 获取答复结束段
        const QString title = model->get_title()
                                  .replace(REPLACE_NAME_FLAG, name)
                                  .replace(REPLACE_SCORE_FLAG, score);  // 获取答复开头段

        // 定制答复错题列表
        QVector<QString> list;
        const auto &error_list = data->at(i).get_error_list();
        for (const auto num : error_list) {
            QString error_item = model->get_error_map().value(QString::number(num));
            if (error_item.size() > 5) {
                list.append(error_item.replace(REPLACE_NAME_FLAG, name));
            }

            // 处理发现不存在的错题号的问题
            if (error_item.isEmpty() && this->_is_ignore == false) {
                send_ask_ignore(QString::number(num));
                return;
            }
        }

        QFile file(QString("%1/%2.docx").arg(gen_dir, name));
        file.open(QIODevice::WriteOnly);
        file.write(title.toUtf8());
        for (const auto &item : list) {
            file.write(item.toUtf8());
        }

        if (!end.isEmpty()) {
            end.replace(REPLACE_NAME_FLAG, name);
            file.write(end.toUtf8());
        }

        emit send_message("文件生成成功：" + name + ".docx...." +
                          QString::number((i + 1) * 100 / student_num) + "%");

        emit send_precent((i + 1) * 100 / student_num);
    }

    emit send_message("共生成文件：" + QString::number(student_num) + "个");

    _is_ignore = false;
    emit finish();
    emit send_message("文件生成完成！");
}

/**
 * @brief 开始生成文件
 *
 * 检查参数合法性后，读取模型文件和数据文件，并生成答复文件。
 *
 * @param model_file_path 模板文件路径
 * @param data_file_path 数据文件路径
 * @param gen_dir 生成文件根路径
 */
void generate_file::start_generate_file(const QString &model_file_path,
                                        const QString &data_file_path,
                                        const QString &gen_dir) {
    // 检查参数合法性
    if (model_file_path.isEmpty() || data_file_path.isEmpty() || gen_dir.isEmpty()) {
        emit file_path_empty();
        return;
    }

    // 读取模板文件
    const model_info *model_text = _read_model_file(model_file_path);
    if (model_text == nullptr) {
        return;
    }

    // 读取数据文件
    const QVector<student_info> *data_text = _read_data_file(data_file_path);
    if (data_text == nullptr) {
        return;
    }

    // 创建新文件
    _generate_file(model_text, data_text, gen_dir);

    delete model_text;
    delete data_text;
}

/**
 * @brief 重新生成文件
 *
 * 根据提供的模型文件路径、数据文件路径和生成目录，重新生成文件。
 * 如果存在未解决的错题号并且未设置忽略标志，则生成失败并发送失败消息。
 *
 * @param model_file_path 模型文件的路径
 * @param data_file_path 数据文件的路径
 * @param gen_dir 生成文件的目录
 * @param is_ignore 是否忽略未解决的错题号
 */
void generate_file::start_re_generate_file(const QString &model_file_path,
                                           const QString &data_file_path,
                                           const QString &gen_dir,
                                           bool is_ignore) {
    if (is_ignore) {
        this->_is_ignore = is_ignore;
        start_generate_file(model_file_path, data_file_path, gen_dir);
    } else {
        emit send_message("有未解决的错题号，生成失败！");
    }
}
